第一部分 预备知识
导入表主要存在于动态链接库文件中,用于将dll文件中的函数导入到外部,给其他的exe或者dll文件调用。我们在导入表一章中知道了程序在装载过程中,通过在INT获得的函数地址覆盖到IAT中,此时,导出表起到了参照和指引的作用。
第二部分 导出表数据结构
1.定位导出表
导出表数据在数据目录的第一个目录中,定位方法和获取导入表的一样。
2.导出目录IMAGE_EXPORT_DIRECTORY
第一个结构是导出表描述符,导入表的IMAGE_EXPORT_DIRECTORY只有一个。下面是其详细定义:
- Name:文件最初的文件名
- NumberOfFunctions:导出函数的个数
- NumberOfNmaes:导出函数其中有名字的个数,NumberOfNames的值小于NumberOfFunctions
- AddressOfFunctions:该指针指向的是所有导出函数的入口地址的起始。函数地址顺序按照函数的编号排序。
- Base:导出函数编号得起始值,第一个导出函数的编号不是从0开始的,某个导出函数的编号等于base+AddressOfFunctions的所在编号。
- AddressOfName:该值是一个指针,指向的位置是连续的双字节,这些双字节指向的是函数名字符串的地址。
- AddressOfNameOrdinals:指向的是函数数字编号得地址。
3.导出表的应用
导出表常见的主要应用是对导出表函数的覆盖和对dll文件内部函数的导出。
3.1.导出函数覆盖
- 修改导出表里面函数的地址。
- 覆盖函数地址部分函数代码。
3.2.导出私有函数
- 获取导出函数地址时
- 在AddressOfNames中找到对应的名字,比如Func2,他在AddressOfNames中是第二项
- 然后从AddressOfNameOrdinals中取出第二项的值,这里是2,表示函数入口保存在AddressOfFunctions这个数组中下标为2的项里,
- 取出AddressOfFunctions里面对应AddressOfNameOrdinals的索引值。
- 加上模块基地址便是导出函数的地址。
https://blog.csdn.net/evileagle/article/details/1217679712345678910111213141516171819202122232425262728293031DWORD* CEAT::SearchEAT( const char* szName){if (IS_VALID_PTR(m_pTable)){bool bByOrdinal = HIWORD(szName) == 0;DWORD* pProcs = (DWORD*)((char*)RVA2VA(m_pTable->AddressOfFunctions));if (bByOrdinal){DWORD dwOrdinal = (DWORD)szName;if (dwOrdinal < m_pTable->NumberOfFunctions && dwOrdinal >= m_pTable->Base){return &pProcs[dwOrdinal-m_pTable->Base];}}else{WORD* pOrdinals = (WORD*)((char*)RVA2VA(m_pTable->AddressOfNameOrdinals));DWORD* pNames = (DWORD*)((char*)RVA2VA(m_pTable->AddressOfNames));for (unsigned int i=0; i<m_pTable->NumberOfNames; ++i){char* pNameVA = (char*)RVA2VA(pNames[i]);if (strcmp(szName, pNameVA) != 0){continue;}return &pProcs[pOrdinals[i]];}}}return NULL;}